// ---------------------------------------------------------------------------------------------------------------------
// DEBUGGER.h
// ---------------------------------------------------------------------------------------------------------------------
// Project:	Alpha Branch Predictor
// Authors:	Jabeer Ahmed, 
// 			Omar Mohsin, 
// 			Fadi Butti
// Course:	ECE - 586 (Winter 2013)
// Prof:	Mark Faust
// Portland State University
// ---------------------------------------------------------------------------------------------------------------------

#include "types.h"
#include <stdlib.h>
/* 
---------------------------------------------------------------------------------------------------------------------
Redefinition of branch_record_c & op_state_c
---------------------------------------------------------------------------------------------------------------------
These classes will defined only if the debugger is compiled without the framework 
for debugging purpose. This will allow for the debugger to read the ASCII text file 
without the need to compile the framework.
***** ASSUMING THAT tread.h & op_state.h WILL BE COMPILED BEFORE THIS FILE *****
*/
#ifndef TREAD_H_SEEN
struct branch_record_c
{
    uint   instruction_addr;       // the branch's PC (program counter)                      
    uint   instruction_next_addr;  // the PC of the static instruction following the branch  
    bool   is_indirect;            // true if the target is computed; false if it's PC-rel; returns are also considered indirect
    bool   is_conditional;         // true if the branch is conditional; false otherwise     
    bool   is_call;                // true if the branch is a call; false otherwise          
    bool   is_return;              // true if the branch is a return; false otherwise        
    bool   is_taken;
    branch_record_c(){
        instruction_addr        = 0;     
        instruction_next_addr   = 0; 
        is_indirect             = false;          
        is_conditional          = false;       
        is_call                 = false;             
        is_return               = false;
        is_taken                = false;
    }
};

struct op_state_c
{
	/* data */
};
#endif

/* 
---------------------------------------------------------------------------------------------------------------------
Debugger & Statistics   
---------------------------------------------------------------------------------------------------------------------
*/

#ifndef __DEBUGGER_H__
#define __DEBUGGER_H__

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#ifdef TREAD_H_SEEN					// if the debugger is used with the framework
#include "tread.h"					// we need to include tread.h
#endif						

#define TF(tf) (tf) ? "True" : "False"
using namespace std;
struct br_stat
{
	uint num_indirect_br;
	uint num_pc_rel_br;
	uint num_conditional_br;
	uint num_unconditional_br;
	uint num_calls;
	uint num_returns;
	int  call_stack;
	uint total_call_stack;
	float avg_call_stack;
	uint max_call_stack;	
	int min_call_stack;
	
	br_stat(): 
		num_indirect_br(0),
		num_pc_rel_br(0),
		num_conditional_br(0),
		num_unconditional_br(0),
		num_calls(0),
		num_returns(0),
		call_stack(12),
		total_call_stack(0),
		avg_call_stack(0),
		max_call_stack(0),	
        min_call_stack(0){}
};

void write_statistics(const char* file, const br_stat* stat)
{
	ofstream stat_file ("stat.txt");
	if (stat_file.is_open()){
		stat_file << "Branch Statistics" << endl;
		stat_file << "num_indirect_br:\t\t" << stat->num_indirect_br << endl;
		stat_file << "num_pc_rel_br:\t\t\t" << stat->num_pc_rel_br << endl;
		stat_file << "num_conditional_br:\t\t" << stat->num_conditional_br << endl;
		stat_file << "num_unconditional_br:\t" << stat->num_unconditional_br << endl;
		stat_file << "num_calls:\t\t\t\t" << stat->num_calls << endl;
		stat_file << "num_returns:\t\t\t" << stat->num_returns << endl;
		stat_file << "call_stack:\t\t\t\t" << stat->call_stack << endl;
		stat_file << "total_call_stack:\t\t" << stat->total_call_stack << endl;
		stat_file << "avg_call_stack:\t\t\t" << stat->avg_call_stack << endl;
		stat_file << "max_call_stack:\t\t\t" << stat->max_call_stack << endl;	
		stat_file << "min_call_stack:\t\t\t" << stat->min_call_stack << endl;	
		stat_file.close();
	}
}

bool initialize_debugger(const char* file)
{
	FILE *fp = fopen(file, "w");
	bool res = false;
	if (fp){
		fprintf(fp, "Branch_PC\t\tactual_target\tis_cond\t\tis_call\t\tis_return\tis_taken\tis_indir\n");
		res = true;
	}
	fclose(fp);	
	return res;
}

void write_prediction(const char* file, const branch_record_c* br, bool taken, uint actual_target_address)
{
	FILE *fp = fopen(file,"a");
	if (fp){
		fprintf(fp, "%p\t\t%p\t\t%s\t\t%s\t\t%s\t\t%s\t\t%s\n", br->instruction_addr, (int)actual_target_address, TF(br->is_conditional),TF(br->is_call),TF(br->is_return),TF(taken),TF(br->is_indirect));
	}
	fclose(fp);
}

#ifndef TREAD_H_SEEN
void initialize_reader(ifstream& file)
{
	string line;
	if (file.is_open() && file.good()){
		getline(file,line);
	}
}

void read_prediction(ifstream& file, branch_record_c* br)
{
    uint target, pc;
    char tf[5][8];
    string line;
	if (file.is_open() && file.good()){
		getline(file,line);
        sscanf(line.c_str(), "%x\t\t%x\t\t%s\t\t%s\t\t%s\t\t%s\t\t%s\n",&pc,&target,tf[0],tf[1],tf[2],tf[3],tf[4]);
        br->instruction_addr = pc;
        br->instruction_next_addr = target;
        br->is_conditional  = tf[0][0] == 'T';
        br->is_call         = tf[1][0] == 'T';
        br->is_return       = tf[2][0] == 'T';        
        br->is_taken        = tf[3][0] == 'T';
        br->is_indirect     = tf[4][0] == 'T';
    }
}
#endif

#endif // __DEBUGGER_H__